/**************************************************************************
 *
 * Copyright (C) 2007 University of California, Los Angeles.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 *************************************************************************/

package edu.ucla.cs.typecast.rmi;

import java.lang.reflect.*;
import java.io.*;
import java.net.*;

import edu.ucla.cs.typecast.bloomfilter.BloomFilter;
import edu.ucla.cs.typecast.bloomfilter.BloomFilterEncoder;
import edu.ucla.cs.typecast.net.EndPointListener;
import edu.ucla.cs.typecast.net.TransportSender;
import edu.ucla.cs.typecast.net.TypeCastDispatcher;
import edu.ucla.cs.typecast.rmi.runtime.RMIUtil;
import edu.ucla.cs.typecast.util.TypeUtil;


public class TypeCastClientHandler implements InvocationHandler, Serializable {
	private static final long serialVersionUID = 1L;
	private InvocationMarshaller marshaller; 
	private TransportSender transportSender; 
	private TypeCastClient typecastClient;   // we need this to prevent the socket to be closed. 
	public TypeCastClientHandler(TypeCastClient client, TransportSender transportSender) {
		this.typecastClient = client; 
		this.transportSender = transportSender; 
	}

	public void setInvocationMarshaller(InvocationMarshaller marshaller) { 
		this.marshaller = marshaller; 
	}
	
	public Object invoke(Object proxy, Method method, Object[] args) throws IOException {		
		ByteArrayOutputStream bas = new ByteArrayOutputStream();
		if (marshaller == null) { 
			marshaller = new InvocationMarshaller(); 
		}		
		BloomFilter filter; 
		Class[] targetTypes; 
		if (RMIUtil.isMetaMethod(method)) { 
			targetTypes = RMIUtil.getTargetTypes(method, args); 
		} else { 
			targetTypes = proxy.getClass().getInterfaces(); 
		}

		filter = BloomFilterEncoder.encode(TypeUtil.getAllTypes(targetTypes)); 		
		
		Class returnType = method.getReturnType(); 
		Object result = null; 
		MethodInvocation invocation; 
		if (returnType != null && Future.class.isAssignableFrom(returnType)) { 
			ReturnSet future = typecastClient.createNewFutureForReturnSet();	
			TypeCastDispatcher dispatcher = new WeakReferencedTypeCastDispatcher(typecastClient);
			EndPointListener endPointListener = transportSender.getLocalEndPointListener(); 
			endPointListener.setTypeCastDispatcher(dispatcher); 
			//@TODO: find the local EndPoint of the listener
			endPointListener.activate(null);
			result = future; 
			invocation = new MethodInvocation(targetTypes, method, args, future.getCorrelationID()); 			
		} else { 
			invocation = new MethodInvocation(targetTypes, method, args); 
		}
		marshaller.marshall(bas, invocation); 				
		transportSender.send(filter, bas.toByteArray(), 0, bas.size()); 
		return result;
	}
}
